package com.fy.service.impl;

// Spring 相关

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.aliyun.ocr_api20210707.Client;
import com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest;
import com.aliyun.ocr_api20210707.models.RecognizeAllTextResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import com.fy.common.config.AliYunAiApi;
import com.fy.common.config.CozeConfig;
import com.fy.mapper.FileInfoMapper;
import com.fy.pojo.entity.FileInfo;
import com.fy.pojo.enums.FileDelFlagEnums;
import com.fy.pojo.enums.FileTypeEnums;
import com.fy.service.AIService;
import com.fy.utils.DocumentConverter;
import com.fy.utils.StringTools;
import com.fy.utils.fileUtils.FileUploadTool;
import com.fy.utils.result.Result;
import io.reactivex.rxjava3.core.Observable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import ws.schild.jave.Encoder;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;

import javax.annotation.Resource;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AIServiceImpl implements AIService {

  @Autowired
  @Qualifier("documentRedisTemplate")
  private RedisTemplate<String, byte[]> redisTemplate;

  @Autowired
  private FileUploadTool fileUploadTool;

  @Resource
  private AliYunAiApi aliYunAiApi;

  @Resource
  private FileInfoMapper fileInfoMapper;

  @Resource
  private CozeConfig cozeConfig;

  @Autowired
  private Client ocrClient;

  /**
   * 基于SSE + RxJava的流式对话
   *
   * @param message 用户消息
   * @return Observable<String> 响应流
   */
  public Observable<String> streamChat(String message) {
    return Observable.create(emitter -> {
      String url = cozeConfig.getBaseUrl();

      Map<String, Object> requestBody = new HashMap<>();
      requestBody.put("model", "qwen-turbo");
      requestBody.put("input", new HashMap<String, Object>() {
        {
          put("messages", new Object[] {
              new HashMap<String, String>() {
                {
                  put("role", "user");
                  put("content", message);
                }
              }
          });
        }
      });
      requestBody.put("parameters", new HashMap<String, Object>() {
        {
          put("incremental_output", true);
        }
      });

      try {
        // 获取响应流
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(
                HttpRequest.post(url)
                    .header("Authorization", cozeConfig.getApiKey())
                    .header("Content-Type", "application/json")
                    .header("Accept", "text/event-stream")
                    .body(JSONUtil.toJsonStr(requestBody))
                    .executeAsync()
                    .bodyStream()));

        // 处理响应流
        String line;
        while ((line = reader.readLine()) != null) {
          if (line.startsWith("data:")) {
            String data = line.substring(5).trim();
            if (!"[DONE]".equals(data)) {
              try {
                String text = JSONUtil.parseObj(data)
                    .getByPath("output.text", String.class);
                if (text != null) {
                  emitter.onNext(text);
                }
              } catch (Exception e) {
                log.error("解析响应异常", e);
              }
            }
          }
        }

        emitter.onComplete();
        reader.close();
      } catch (Exception e) {
        log.error("API调用异常", e);
        emitter.onError(e);
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<byte[]> wordToPdf(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "doc:pdf:" + file.getOriginalFilename();
        // 检查缓存
        byte[] cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
          log.info("从缓存获取PDF文件: {}", file.getOriginalFilename());
          return cached;
        }

        // 创建临时文件
        File inputWord = File.createTempFile("input", ".docx");
        File outputPdf = File.createTempFile("output", ".pdf");
        log.info("创建临时文件 - 输入: {}, 输出: {}", inputWord.getPath(), outputPdf.getPath());

        try {
          // 将上传的文件内容写入临时文件
          FileUtils.writeByteArrayToFile(inputWord, file.getBytes());

          // 使用documents4j进行转换
          IConverter converter = LocalConverter.builder()
              .baseFolder(inputWord.getParentFile())
              .build();

          log.info("开始转换文档: {}", file.getOriginalFilename());
          Future<Boolean> conversion = converter.convert(inputWord).as(DocumentType.MS_WORD)
              .to(outputPdf).as(DocumentType.PDF)
              .prioritizeWith(1000) // 优先级
              .schedule();

          // 增加超时时间到30秒
          if (!conversion.get(30, TimeUnit.SECONDS)) {
            throw new RuntimeException("文档转换失败");
          }

          // 读取转换后的PDF
          byte[] pdfContent = FileUtils.readFileToByteArray(outputPdf);
          log.info("文档转换成功，PDF大小: {} bytes", pdfContent.length);

          // 缓存结果
          redisTemplate.opsForValue().set(cacheKey, pdfContent, 1, TimeUnit.HOURS);

          return pdfContent;

        } finally {
          // 清理临时文件
          try {
            if (inputWord.exists()) {
              inputWord.delete();
            }
            if (outputPdf.exists()) {
              outputPdf.delete();
            }
            log.info("清理临时文件完成");
          } catch (Exception e) {
            log.warn("清理临时文件失败", e);
          }
        }

      } catch (TimeoutException e) {
        log.error("文档转换超时", e);
        throw new RuntimeException("文档转换超时，请稍后重试或尝试转换较小的文件");
      } catch (Exception e) {
        log.error("Word转PDF失败", e);
        throw new RuntimeException("文档转换失败: " + e.getMessage());
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<byte[]> excelToPdf(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "excel:pdf:" + file.getOriginalFilename();
        // 检查缓存
        byte[] cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
          return cached;
        }

        XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        workbook.write(out);
        workbook.close();

        byte[] result = out.toByteArray();
        // 存入缓存
        redisTemplate.opsForValue().set(cacheKey, result, 1, TimeUnit.HOURS);

        return result;
      } catch (IOException e) {
        log.error("Excel转PDF失败", e);
        throw new RuntimeException("文档转换失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<String> wordToHtml(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "doc:html:" + file.getOriginalFilename();
        // 检查缓存
        byte[] cachedBytes = redisTemplate.opsForValue().get(cacheKey);
        if (cachedBytes != null) {
          return new String(cachedBytes, StandardCharsets.UTF_8);
        }

        XWPFDocument document = new XWPFDocument(file.getInputStream());
        String html = DocumentConverter.convertWordToHtml(document);
        document.close();

        // 存入缓存
        redisTemplate.opsForValue().set(cacheKey, html.getBytes(StandardCharsets.UTF_8), 1, TimeUnit.HOURS);

        return html;
      } catch (IOException e) {
        log.error("Word转HTML失败", e);
        throw new RuntimeException("文档转换失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<String> excelToHtml(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "excel:html:" + file.getOriginalFilename();
        // 检查缓存
        byte[] cachedBytes = redisTemplate.opsForValue().get(cacheKey);
        if (cachedBytes != null) {
          return new String(cachedBytes, StandardCharsets.UTF_8);
        }

        XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
        String html = DocumentConverter.convertExcelToHtml(workbook);
        workbook.close();

        // 存入缓存
        redisTemplate.opsForValue().set(cacheKey, html.getBytes(StandardCharsets.UTF_8), 1, TimeUnit.HOURS);

        return html;
      } catch (IOException e) {
        log.error("Excel转HTML失败", e);
        throw new RuntimeException("文档转换失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<String> speechToText(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "audio:text:" + file.getOriginalFilename();
        // 检查缓存
        byte[] cachedBytes = redisTemplate.opsForValue().get(cacheKey);
        if (cachedBytes != null) {
          return new String(cachedBytes, StandardCharsets.UTF_8);
        }

        // 创建临时文件
        File audioFile = File.createTempFile("speech", ".wav");
        try {
          file.transferTo(audioFile);

          // 使用 JavaSound API 进行音频分析
          AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(audioFile);
          AudioFormat format = audioInputStream.getFormat();

          // 获取音频的基本信息
          StringBuilder info = new StringBuilder();
          info.append("音频信息：\n");
          info.append("采样率：").append(format.getSampleRate()).append(" Hz\n");
          info.append("位深度：").append(format.getSampleSizeInBits()).append(" bits\n");
          info.append("声道数：").append(format.getChannels()).append("\n");
          info.append("编码方式：").append(format.getEncoding()).append("\n");

          // 分析音频波形
          info.append("\n波形分析：\n");
          byte[] audioData = new byte[1024];
          int maxAmplitude = 0;
          int totalRead = 0;
          int numRead = 0;
          while ((numRead = audioInputStream.read(audioData)) != -1) {
            for (int i = 0; i < numRead; i++) {
              maxAmplitude = Math.max(maxAmplitude, Math.abs(audioData[i]));
            }
            totalRead += numRead;
          }

          double durationInSeconds = totalRead / (format.getSampleRate() * format.getFrameSize());
          info.append("时长：").append(String.format("%.2f", durationInSeconds)).append(" 秒\n");
          info.append("最大振幅：").append(maxAmplitude).append("\n");
          info.append("平均音量：").append(String.format("%.2f", (double) maxAmplitude / 128)).append(" (0-1范围)\n");

          String text = info.toString();

          // 存入缓存
          redisTemplate.opsForValue().set(cacheKey, text.getBytes(StandardCharsets.UTF_8), 1, TimeUnit.HOURS);

          return text;
        } finally {
          audioFile.delete();
        }
      } catch (Exception e) {
        log.error("音频分析失败", e);
        throw new RuntimeException("音频分析失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<byte[]> convertAudioFormat(MultipartFile file, String format) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        String cacheKey = "audio:convert:" + file.getOriginalFilename() + ":" + format;
        // 检查缓存
        byte[] cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
          return cached;
        }

        // 创建临时文件
        File source = File.createTempFile("input", "." + getExtension(file.getOriginalFilename()));
        File target = File.createTempFile("output", "." + format);

        try {
          // 保存上传的文件
          file.transferTo(source);

          // 设置音频属性
          AudioAttributes audio = new AudioAttributes();
          audio.setCodec("libmp3lame"); // 根据目标格式设置编码器
          audio.setBitRate(128000);
          audio.setChannels(2);
          audio.setSamplingRate(44100);

          // 设置转换属性
          EncodingAttributes attrs = new EncodingAttributes();
          attrs.setAudioAttributes(audio);

          // 执行转换
          Encoder encoder = new Encoder();
          encoder.encode(new MultimediaObject(source), target, attrs);

          // 读取转换后的文件
          byte[] result = FileUtils.readFileToByteArray(target);

          // 存入缓存
          redisTemplate.opsForValue().set(cacheKey, result, 1, TimeUnit.HOURS);

          return result;
        } finally {
          source.delete();
          target.delete();
        }
      } catch (Exception e) {
        log.error("音频格式转换失败", e);
        throw new RuntimeException("音频转换失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<Long> getAudioDuration(MultipartFile file) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        File tempFile = File.createTempFile("duration", "." + getExtension(file.getOriginalFilename()));
        try {
          file.transferTo(tempFile);
          MultimediaObject multimedia = new MultimediaObject(tempFile);
          return multimedia.getInfo().getDuration();
        } finally {
          tempFile.delete();
        }
      } catch (Exception e) {
        log.error("获取音频时长失败", e);
        throw new RuntimeException("获取时长失败");
      }
    });
  }

  private String getExtension(String filename) {
    if (filename == null || filename.lastIndexOf(".") == -1) {
      return "jpg"; // 默认扩展名
    }
    return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<Map<String, Object>> analyzeText(String text) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        Map<String, Object> result = new HashMap<>();

        // 1. 字数统计
        result.put("wordCount", text.length());

        // 2. 关键词提取 (简单实现)
        String[] words = text.split("\\s+");
        Map<String, Integer> wordFreq = new HashMap<>();
        for (String word : words) {
          wordFreq.merge(word, 1, Integer::sum);
        }
        result.put("keywords", wordFreq.entrySet().stream()
            .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
            .limit(5)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList()));

        // 3. 生成摘要 (简单实现)
        String summary = text.length() > 100 ? text.substring(0, 100) + "..." : text;
        result.put("summary", summary);

        return result;
      } catch (Exception e) {
        log.error("文本分析失败", e);
        throw new RuntimeException("文本分析失败");
      }
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<String> translateText(String text, String targetLang) {
    return CompletableFuture.supplyAsync(() -> {
      // 简单实现，仅返回原文
      return "翻译功能暂未实现";
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<Boolean> isTextSafe(String text) {
    return CompletableFuture.supplyAsync(() -> {
      // 简单实现，默认安全
      return true;
    });
  }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<byte[]> compressImage(MultipartFile file, double quality) {
    return CompletableFuture.supplyAsync(() -> {
      throw new RuntimeException("图片压缩功能暂未实现");
    });
  }

  // @Override
  // @Async("documentTaskExecutor")
  // public CompletableFuture<byte[]> addWatermark(MultipartFile file, String
  // text,
  // String gravity, Integer dissolve, Integer dx, Integer dy) {
  // return CompletableFuture.supplyAsync(() -> {
  // try {
  // // 1. 上传原图到临时目录
  // String originalFilename = file.getOriginalFilename();
  // String extension =
  // originalFilename.substring(originalFilename.lastIndexOf("."));
  // String tempPath = "temp/" + UUID.randomUUID().toString() + extension;
  //
  // // 2. 上传到OSS并获取URL
  // String imageUrl = aliOssUtil.uploadImg(file);
  //
  // // 3. 构建水印处理参数
  // StringBuilder processParam = new StringBuilder();
  // processParam.append("watermark/2"); // 文字水印
  // processParam.append("/text/").append(Base64.encodeBase64URLSafeString(text.getBytes("UTF-8")));
  // processParam.append("/gravity/").append(gravity);
  // processParam.append("/dissolve/").append(dissolve);
  // processParam.append("/dx/").append(dx);
  // processParam.append("/dy/").append(dy);
  //
  // // 4. 处理图片
  // String processedUrl = imageUrl + "?" + processParam.toString();
  //
  // // 5. 下载处理后的图片
  // URL url = new URL(processedUrl);
  // HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  // try (InputStream in = conn.getInputStream()) {
  // return IOUtils.toByteArray(in);
  // }
  //
  // } catch (Exception e) {
  // log.error("添加水印失败", e);
  // throw new RuntimeException("添加水印失败", e);
  // }
  // });
  // }

  @Override
  @Async("documentTaskExecutor")
  public CompletableFuture<byte[]> convertImageFormat(MultipartFile file, String format) {
    return CompletableFuture.supplyAsync(() -> {
      throw new RuntimeException("图片格式转换功能暂未实现");
    });
  }

  /**
   * 图片文字识别
   * 
   * @param file
   * @return
   */

  @Override
  public Result<?> ocr(MultipartFile file, String fileMD5) {
    String filePath = null;
    boolean needDelete = false;

    try {
      // 0. 验证文件格式
      String contentType = file.getContentType();
      if (contentType == null || !contentType.startsWith("image/")) {
        log.error("不支持的文件格式: {}", contentType);
        return Result.error("只支持图片文件格式(jpg/png/jpeg等)");
      }

      // 1. 先查找是否已有相同MD5的图片文件
      LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
      wrapper.eq(FileInfo::getFileMd5, fileMD5)
          .eq(FileInfo::getFileType, FileTypeEnums.IMAGE.getType())
          .eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag())
          .or()
          .eq(FileInfo::getDelFlag, FileDelFlagEnums.RECYCLE.getFlag())
          .eq(FileInfo::getFileType, FileTypeEnums.IMAGE.getType());

      List<FileInfo> fileInfos = fileInfoMapper.selectList(wrapper);

      // 2. 获取文件路径
      if (fileInfos != null && !fileInfos.isEmpty()) {
        FileInfo fileInfo = fileInfos.get(0);
        // 再次验证文件类型
        if (fileInfo.getFileType() != FileTypeEnums.IMAGE.getType()) {
          return Result.error("只支持图片文件格式(jpg/png/jpeg等)");
        }
        filePath = fileInfo.getFilePath();
        log.info("使用已存在的文件进行OCR识别, filePath: {}", filePath);
      } else {
        filePath = fileUploadTool.uploadFile(file);
        needDelete = true;
        log.info("上传新文件进行OCR识别, filePath: {}", filePath);
      }

      // 3. 创建OCR请求
      RecognizeAllTextRequest request = new RecognizeAllTextRequest()
          .setType("Advanced")
          .setUrl(filePath)
          .setOutputOricoord(true);

      // 4. 设置高级配置
      RecognizeAllTextRequest.RecognizeAllTextRequestAdvancedConfig advancedConfig = new RecognizeAllTextRequest.RecognizeAllTextRequestAdvancedConfig()
          .setOutputCharInfo(true);
      request.setAdvancedConfig(advancedConfig);

      // 5. 调用OCR服务
      log.info("开始调用OCR服务...");
      RecognizeAllTextResponse response = ocrClient.recognizeAllText(request);

      // 6. 处理结果
      if (response != null && response.getBody() != null && response.getBody().getData() != null) {
        String content = response.getBody().getData().getContent();
        log.info("OCR识别成功，文本长度: {}", content.length());
        return Result.success(content);
      }

      log.warn("OCR识别未返回有效内容");
      return Result.error("未识别到文字内容");

    } catch (Exception e) {
      log.error("OCR识别失败", e);
      return Result.error("OCR识别失败：" + e.getMessage());

    } finally {
      // 7. 如果是新上传的文件且没有其他用户使用，则删除
      if (needDelete && filePath != null) {
        try {
          String fileName = StringTools.extractFileName(filePath);
          log.info("清理临时文件: {}", fileName);
          fileUploadTool.deleteFile(fileName);
        } catch (Exception e) {
          log.error("删除临时文件失败", e);
        }
      }
    }
  }
}